package com.googlecode.YaccSyntaxTool;

import java.io.FileNotFoundException;
import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;

import com.googlecode.YaccSyntaxTool.export.Exporter;
import com.googlecode.YaccSyntaxTool.grammar.Syntax;
import com.googlecode.YaccSyntaxTool.parser.YOutputParser;

public class YaccSyntaxTool {
	
	private final static String		VERSION = "pre-release";

	private final static String		CL_OPTION_FILENAME		= "f";
	private final static String		CL_OPTION_FILENAME_L	= "filename";
	private final static String		CL_OPTION_FILENAME_DF	= "./input/y.output.syntax";
	private final static String		CL_OPTION_OUTPUTDIR		= "o";
	private final static String		CL_OPTION_OUTPUTDIR_L	= "outputdir";
	private	 final static String		CL_OPTION_OUTPUTDIR_DF	= "./output";
	private final static String		CL_OPTION_SIMPLIFY		= "s";
	private final static String		CL_OPTION_SIMPLIFY_L	= "simplify";
	private final static String		CL_OPTION_HELP			= "h";
	private final static String		CL_OPTION_HELP_L		= "help";
	private final static String		CL_OPTION_VERSION_L		= "version";


	/**
	 * Logger
	 */
	private final static Logger sLog = Logger.getLogger(YaccSyntaxTool.class.getName());

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		CommandLineParser clParser = new GnuParser();
		CommandLine cl = null;
		YaccSyntaxTool tool = new YaccSyntaxTool();

		Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).setLevel(Level.FINE);

		// handle command line options
		Options clOptions = getCLOptions();
		boolean printHelp = false;
		try {
			cl = clParser.parse(clOptions, args, false);
		} catch (org.apache.commons.cli.ParseException e) {
			printHelp = true;
		}

		if (printHelp || cl.hasOption(CL_OPTION_HELP)) {
			new HelpFormatter().printHelp("YaccSyntaxTool.jar [OPTIONS]", clOptions);
			System.exit(0);
		} else if (cl.hasOption(CL_OPTION_VERSION_L)) {
			System.out.println("YaccSyntaxTool "+VERSION);
			System.out.println("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.");
			System.out.println("This is free software: you are free to change and redistribute it.");
			System.out.println("There is NO WARRANTY, to the extent permitted by law.");
			System.out.println("");
			System.out.println("Web: http://code.google.com/p/yacc-syntax-tool/");
			System.exit(0);
		}
		
		tool.handleSyntax(cl);
	}

	public YaccSyntaxTool() {
		super();
	}

	private void handleSyntax(CommandLine cl) {
		String filename = cl.getOptionValue(CL_OPTION_FILENAME, CL_OPTION_FILENAME_DF);
		String outputDir = cl.getOptionValue(CL_OPTION_OUTPUTDIR, CL_OPTION_OUTPUTDIR_DF);
		
		
		YOutputParser parser = new YOutputParser();

		Syntax		syntax = null;

		// parse the BNF syntax
		try {
			syntax = parser.parse(filename);
		} catch (FileNotFoundException e) {
			sLog.log(Level.SEVERE,"Could not find file: "+filename);
			sLog.log(Level.SEVERE,"Current working directory: "+System.getProperty("user.dir"));
		} catch (ParseException e) {
			sLog.log(Level.SEVERE,"Error on parsing the file: "+e.getMessage(),e);
		}

		if (syntax != null) {
			Exporter exporter = new Exporter(outputDir);
			
			exporter.log(syntax);

			exporter.exportHtml(syntax);

			// EXPERIMENTAL: CONVERT BNF TO EBNF
			if (cl.hasOption(CL_OPTION_SIMPLIFY)) {
				syntax.simplify();
				exporter.log(syntax);
			}
		}
	}
	
	static private Options getCLOptions() {
		Options clOptions = new Options();
		
		clOptions.addOption(CL_OPTION_FILENAME, CL_OPTION_FILENAME_L, true, "Y file that should be parsed");
		clOptions.addOption(CL_OPTION_OUTPUTDIR, CL_OPTION_OUTPUTDIR_L, true, "Export HTML to this directory");
		clOptions.addOption(CL_OPTION_SIMPLIFY, CL_OPTION_SIMPLIFY_L, false, "Convert syntax from BNF to EBNF");
		clOptions.addOption(CL_OPTION_HELP, CL_OPTION_HELP_L, false, "Show this help and exit");
		clOptions.addOption(null, "version", false, "Show version and exit");
		
		return clOptions;
	}
}
